package com.zoe.onelink.auth.autoconfigure;

import com.zoe.onelink.auth.property.OnelinkAuthProperties;
import com.zoe.onelink.auth.token.authenticate.OnelinkTokenAuthenticate;
import com.zoe.onelink.auth.token.authenticate.OnelinkTokenAuthenticateCallback;
import com.zoe.onelink.auth.token.fallback.DefaultOnelinkTokenFallbackProviderImpl;
import com.zoe.onelink.auth.token.processor.OnelinkTokenFallbackProvider;
import com.zoe.onelink.auth.token.processor.OnelinkTokenInfoProcessor;
import com.zoe.onelink.auth.token.processor.impl.OnelinkCircuitBreakerTokenInfoProcessor;
import com.zoe.onelink.auth.token.processor.impl.OnelinkJsonTokenInfoProcessor;
import com.zoe.onelink.auth.token.processor.impl.OnelinkLocalCachedRedisTokenInfoProcessor;
import com.zoe.onelink.auth.token.processor.impl.OnelinkRedisTokenInfoProcessor;
import com.zoe.onelink.redis.util.LettuceWatcherUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-05-11
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(OnelinkAuthProperties.class)
@RequiredArgsConstructor
@Slf4j
public class OnelinkAuthAutoConfiguration implements WebMvcConfigurer {

    @Bean
    @ConditionalOnMissingBean(OnelinkTokenFallbackProvider.class)
    @ConditionalOnProperty(prefix = OnelinkAuthProperties.PREFIX, name = "fallback-enabled", matchIfMissing = true)
    public OnelinkTokenFallbackProvider bizTokenSelfProtectProvider(OnelinkAuthProperties onelinkAuthProperties) {
        return new DefaultOnelinkTokenFallbackProviderImpl(onelinkAuthProperties);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = OnelinkAuthProperties.PREFIX, name = "strategy", havingValue = "ENCRYPT_ALGORITHM", matchIfMissing = true)
    public OnelinkTokenInfoProcessor onelinkDesTokenInfoProcessor(OnelinkAuthProperties onelinkAuthProperties) {
        log.info("[OneLinkFrm] [OnelinkToken解析器] 基于加密算法解析", onelinkAuthProperties.getLocalCache().getExpiredTime());
        return new OnelinkJsonTokenInfoProcessor(onelinkAuthProperties.getTokenSecretKey());
    }


    @Bean
    public OnelinkTokenAuthenticate onelinkTokenAuthenticate(
            OnelinkTokenInfoProcessor onelinkTokenInfoProcessor,
            List<OnelinkTokenAuthenticateCallback> onelinkTokenAuthenticateCallbacks,
            HttpServletRequest request) {
        return new OnelinkTokenAuthenticate(onelinkTokenInfoProcessor,
                onelinkTokenAuthenticateCallbacks == null ? Collections.emptyList() : onelinkTokenAuthenticateCallbacks,
                request);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({LettuceConnectionFactory.class, RedisTemplate.class, LettuceWatcherUtil.class})
    static class OnelinkAuthRedisAutoConfiguration {

        public static final String ONELINK_TOKEN_DEGRADE_RULE_RESOURCE_NAME = "degrade-onelink-token";

        @Bean
        @Primary
        @ConditionalOnProperty(prefix = OnelinkAuthProperties.PREFIX, name = "strategy", havingValue = "CIRCUIT_BREAKER")
        public OnelinkCircuitBreakerTokenInfoProcessor onelinkCompositeTokenInfoProcessor(
                RedisTemplate<String, Object> redisTemplate,
                OnelinkAuthProperties onelinkAuthProperties,
                ObjectProvider<CircuitBreakerFactory> circuitBreakerFactoryProvider,
                ObjectProvider<OnelinkTokenFallbackProvider> onelinkTokenFallbackProviders) {
            log.info("[OneLinkFrm] [OnelinkToken解析器] 基于REDIS+本地缓存+熔断器获取,本地缓存过期时间:{}", onelinkAuthProperties.getLocalCache().getExpiredTime());
            OnelinkLocalCachedRedisTokenInfoProcessor localCachedProcessor = new OnelinkLocalCachedRedisTokenInfoProcessor(redisTemplate, onelinkAuthProperties);
            CircuitBreakerFactory circuitBreakerFactory = circuitBreakerFactoryProvider.getIfAvailable();
            CircuitBreaker circuitBreaker = circuitBreakerFactory == null ? null : circuitBreakerFactory.create(ONELINK_TOKEN_DEGRADE_RULE_RESOURCE_NAME);
            return new OnelinkCircuitBreakerTokenInfoProcessor(localCachedProcessor, onelinkTokenFallbackProviders.getIfAvailable(), circuitBreaker);
        }

        @Bean
        @ConditionalOnProperty(prefix = OnelinkAuthProperties.PREFIX, name = "strategy", havingValue = "REDIS")
        @Primary
        public OnelinkTokenInfoProcessor onelinkRedisTokenInfoProcessor(RedisTemplate<String, Object> redisTemplate) {
            log.info("[OneLinkFrm] [OnelinkToken解析器] 基于REDIS获取");
            return new OnelinkRedisTokenInfoProcessor(redisTemplate);
        }

        @Bean
        @ConditionalOnProperty(prefix = OnelinkAuthProperties.PREFIX, name = "strategy", havingValue = "REDIS_AND_LOCAL_CACHE", matchIfMissing = true)
        @Primary
        public OnelinkTokenInfoProcessor onelinkLocalCachedRedisTokenInfoProcessor(RedisTemplate<String, Object> redisTemplate,
                                                                                   OnelinkAuthProperties onelinkAuthProperties,
                                                                                   ObjectProvider<OnelinkTokenFallbackProvider> onelinkTokenSelfProtectProviders,
                                                                                   ObjectProvider<CircuitBreakerFactory> circuitBreakerFactoryProviders) {
            log.info("[OneLinkFrm] [OnelinkToken解析器] 基于REDIS+本地缓存获取,本地缓存过期时间:{}", onelinkAuthProperties.getLocalCache().getExpiredTime());
            return new OnelinkLocalCachedRedisTokenInfoProcessor(redisTemplate, onelinkAuthProperties);
        }
    }

}
